﻿#include "Log.h"
#include "StringHelper.h"

namespace sky
{
    void ConsoleAppender::Log(const char *msg, const char *time, LogLevel level)
    {
        assert(msg != NULL);
    
        static const char * color[] = 
        {
            0,
            "\033[1;30m",   // Info
            "\033[1;34m",   // Trace
            0,
            "\033[1;35m",   // Warning
            0,0,0,
            "\033[1;31m",   // Error
            0,0,0,0,0,0,0,
            "\033[1;33;41m",   //
        };

        static const char * endcolor = "\033[0m";


        std::cout << color[(int)level];

        if (time && hasTime())
        {
            std::cout << time;
        }


        // 如果msg结尾有\n，先输出\n 再输出 endcolor 会比较难看，调整一下位置。
        std::string strMsg = msg;
        size_t nLen = strMsg.length();
        if (strMsg.at(nLen - 1) == '\n')
            std::cout << strMsg.substr(0, nLen-1) << endcolor << "\n";
        else
            std::cout << msg << endcolor;
    }

    FileAppender::~FileAppender()
    {

        if (m_File.is_open())
        {
            m_File << "</font>\n</body>\n</html>";
            m_File.close();
        }

    }

    bool FileAppender::create(const char *filename, bool bWrite)
    {
        m_File.open(filename, bWrite ? std::ios::trunc : std::ios::app);

        if (!m_File.is_open())
            return false;

        m_File << "<html>\n" 
            << "<head>\n"
            << "<meta http-equiv=\"content-type\" content=\"text/html; charset=gb2312\">\n"
            << "<title>Html Output</title>\n"
            << "</head>\n"
            << "<body>\n<font face=\"Fixedsys\" size=\"2\" color=\"#0000FF\">\n";



        return true;
    }

    void FileAppender::Log(const char *msg, const char *time, LogLevel level)
    {
        assert(msg != NULL);

        static const char * color[] = 
        {
            0,
            "<font color=\"#000000\">", // Info
            "<font color=\"#0000FF\">",	// Trace
            0,
            "<font color=\"#FF00FF\">",	// Warning
            0,0,0,
            "<font color=\"#FF0000\">",	// Error
            0,0,0,0,0,0,0,
            "<font color=\"#FFFF00\" style =\"background-color:#6a3905;\">",	// Emphasis
        };

        // 颜色
        m_File << color[(int)level];

        //输出时间
        if (time && hasTime())
        {
            m_File << time;
        }

        // 输出信息 (遇\n即换行)
        std::string strMsg = msg;
#ifdef  SUPPORT_STRINGHELPER
        StringHelper::replace(strMsg, "\n", "<br>");
#else
        std::string strOld = "\n";
        std::string strNew = "<br>";
        size_t _Pos = strMsg.find(strOld, 0);
        while (_Pos != std::string::npos)
        {
            strMsg.replace(_Pos, strOld.length(), strNew.c_str(), strNew.length());
            _Pos += 1;
            _Pos = strMsg.find(strOld, _Pos);
        }
#endif

        m_File << strMsg;

        
        // 一条信息结束，加个硬换行
        m_File << "</font>\n";


        m_File.flush();
    }

    Logger::Logger()
    {
    }

    Logger::~Logger()
    {
        close();
    }

    bool Logger::create(int level, bool hasTime)
    {
        this->level = level;
        this->hasTime = hasTime;
        this->hasFileLine = true;
        this->limitFrequency = false;

        this->isExit = false;
        this->hThread = this->hEvent = NULL;
        this->inList = &this->msgs1;
        this->outList = &this->msgs2;
        this->clearOutList = false;
        this->threshold = 1024;

        DWORD dwThread = 0;
        HANDLE hThread = ::CreateThread(NULL, 0, LoggerProc, this, 0, &dwThread);
        if (hThread != NULL)
        {
            // 创建事件
            HANDLE hEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);
            if (hEvent != NULL)
            {
                this->hThread = hThread;
                this->hEvent = hEvent;
                return true;
            }
        }

        return false;
    }

    void Logger::close()
    {
        if (this->hThread)
        {
            this->isExit = true;
            ::SetEvent(this->hEvent);
            ::WaitForSingleObject(this->hThread, 10000L);
            ::CloseHandle(this->hThread);
            this->hThread = NULL;

            // 可能还有输入信息没有输出
            swaplist();
            output_outlist();

            this->mutex.Lock();
            this->mutex.UnLock();

            if (this->hEvent)
                ::CloseHandle(this->hEvent);
        }

        for (Logger::AppenderList::iterator it = this->m_lstAppenders.begin(); 
                it != this->m_lstAppenders.end(); ++it)
        {
            delete *it;
        }

        this->m_lstAppenders.clear();
    }

    void Logger::output(const char *msg, LogLevel level, const char *file, int line)
    {
        if (!msg)
            return;
        
        // 全局Logger设置是否支持该级别的输出
        if ((this->level & (int)level) == 0)
            return;

        // 输出到VC的输出框(不通过线程，立即输出，内置特征)
    #ifdef SKY_DEBUG
        if (IsDebuggerPresent())
        {
            if (this->hasFileLine)
            {
                mostrbuf osb;
                if (!luapath.empty())
                {
                    size_t sz = file ? strlen(file) : 0;
                    if (sz > 4) // for ".lua"
                    {
                        if (file[sz-4] == '.' && strnicmp(file + sz - 4, ".lua", 4) == 0) // for lua filename
                        {
                            osb << luapath.c_str();
                        }
                    }

                    osb << file << "(" << line << "): " << msg;
                    ::OutputDebugString(osb.c_str());
                }
                else
                {
                    ::OutputDebugString(my_utf82t(msg));
                }
            }
        }

    #endif

        // 线程加锁
    #ifdef SKY_DEBUG
        this->mutex.Lock(file, line);
    #else
        this.mutex.Lock();
    #endif

        // 压入一个空消息体到输入队列
        bool discard = true;
        if (this->inList->size() < this->threshold)
        {
            discard = false;
            this->inList->push_back(Logger::_MSG());

            // 填充节点数据
            Logger::_MSG & msgnode = this->inList->back();
            msgnode.level = level;
            msgnode.time = this->hasTime ? time(0) : 0;
            msgnode.msg = msg;
        }

        // 解锁
        this->mutex.UnLock();

        // 设置事件
        if (!discard)
        {
            if (!::SetEvent(this->hEvent))
            {
                DWORD dwErr = ::GetLastError();
            }
        }
        
    }

    void Logger::swaplist()
    {
        // 交换输入/输出队列
    #ifdef SKY_DEBUG
        this->mutex.Lock(__FILE__, __LINE__);
    #else
        this.mutex.Lock();
    #endif

        Logger::MsgList * outList = this->inList;
        this->inList = this->outList;
        this->outList = outList;

        this->mutex.UnLock();
    }

    void Logger::output_outlist()
    {
        Logger::MsgList * outList = this->outList;
        if (!outList->empty())
        {
            // 输出
            char time_str[32] = {0};
            const char * p_time_str = 0;
            for(Logger::MsgList::iterator it = outList->begin(); it != outList->end(); ++it)
            {
                Logger::_MSG & msgnode = *it;
                if (msgnode.time)
                {
                    strftime(time_str, 32, "[%Y-%m-%d %H:%M:%S] ", localtime(&msgnode.time));
                    // strftime(time_str, 32, "[%m%d %H:%M:%S] ", localtime(&msgnode.time));
                    p_time_str = time_str;
                }
                else
                {
                    p_time_str = 0;
                }

                for(Logger::AppenderList::iterator it2 = this->m_lstAppenders.begin();
                    it2 != this->m_lstAppenders.end(); ++it2)
                {
                    LogAppender * obj = *it2;
                    if (obj->getLevel() & msgnode.level)
                        obj->Log(msgnode.msg.c_str(), p_time_str, msgnode.level);
                }
            }
        }
    }

    void Logger::setLogerLuaPath(const std::string &path)
    {
        this->luapath = path;
    }

    int Logger::getLevel() const
    {
        return this->level;
    }

    int Logger::setLevel(int level)
    {
        // this->mutex.Lock();
        // int old = this->level;
        // this->level = level;
        // this->mutex.UnLock();
        scopelock lock(this->mutex);
        int old = this->level;
        this->level = level;

        return old;
    }

    bool Logger::setHasTime(bool hasTime)
    {   
        // this->mutex.Lock();
        // bool old = this->hasTime;
        // this->hasTime = hasTime;
        // this->mutex.UnLock();

        scopelock lock(this->mutex);
        bool old = this->hasTime;
        this->hasTime = hasTime;

        return old;
    }

    bool Logger::setLimitFrequency(bool limitFrequency)
    {
        // this->mutex.Lock();
        // bool old = this->limitFrequency;
        // this->limitFrequency = limitFrequency;
        // this->mutex.UnLock();

        scopelock lock(this->mutex);
        bool old = this->limitFrequency;
        this->limitFrequency = limitFrequency;

        return old;
    }

    bool Logger::hasLimitFrequency()
    {
        return this->limitFrequency;
    }

    size_t Logger::setMessageThreshold(size_t threshold)
    {
        // this->mutex.Lock();
        // size_t old = this->threshold;
        // this->threshold = threshold;
        // this->mutex.UnLock();

        scopelock lock(this->mutex);
        size_t old = this->threshold;
        this->threshold = threshold;

        return old;
    }

    size_t Logger::getMessageThreshold() const
    {
        return this->threshold;
    }

    void Logger::registerAppender(LogAppender *appender)
    {
        if (!appender)
            return;
        // this->mutex.Lock();
        // this->m_lstAppenders.remove(appender); // if exist, remove it
        // this->m_lstAppenders.push_back(appender);
        // this->mutex.UnLock();

        scopelock lock(this->mutex);
        this->m_lstAppenders.remove(appender); // if exist, remove it
        this->m_lstAppenders.push_back(appender);
    }

    void Logger::unRegisterAppender(LogAppender *appender)
    {
        if (!appender) 
            return;
        // this->mutex.Lock();
        // for (Logger::AppenderList::iterator it=this->m_lstAppenders.begin(); it!=this->m_lstAppenders.end(); ++it)
        // {
        //     if (*it == appender)
        //     {
        //         this->m_lstAppenders.erase(it);
        //         break;
        //     }
        // }
        // this->mutex.UnLock();

        scopelock lock(this->mutex);

        for (Logger::AppenderList::iterator it=this->m_lstAppenders.begin(); it!=this->m_lstAppenders.end(); ++it)
        {
            if (*it == appender)
            {
                this->m_lstAppenders.erase(it);
                break;
            }
        }
    }

    DWORD WINAPI Logger::LoggerProc(LPVOID arg)
    {
        Logger * logger = (Logger *)arg;
        if (!logger)
            return 1;
        
        while (!logger->isExit)
        {
            DWORD dwWaitResult = ::WaitForSingleObject(logger->hEvent, 10000L);
            DWORD dwErr = ::GetLastError();
            switch (dwWaitResult)
            {
            case WAIT_OBJECT_0:
                /* code */
                break;
            case WAIT_TIMEOUT:
                break;

            case WAIT_ABANDONED:
                break;
            }

            // 交换输入输出队列
            logger->swaplist();
            logger->output_outlist();

            Sleep(10);
        }
        

        return 0;
    }

    SKY_API void createLogger(int level, bool hasTime, bool threadSafe)
    {
        if (gLogger)
            delete gLogger;
        
        gLogger = new Logger();
        if (!gLogger->create(level, hasTime))
        {
            delete gLogger;
            gLogger = 0;
        }
    }

    SKY_API void closeLogger()
    {
        if (gLogger)
        {
            delete gLogger;
            gLogger = 0;
        }
    }

    SKY_API int setLoggerLevel(int level)
    {
        if (!gLogger)
            createLogger();

        return gLogger ? gLogger->setLevel(level) : 0;
    }

    SKY_API int getLoggerLevel()
    {
        if (!gLogger)
            createLogger();
        
        return gLogger ? gLogger->getLevel() : 0;
    }

    SKY_API bool setLoggerHasTime(bool hasTime)
    {
        if (!gLogger)
            createLogger();
        // gLogger->mutex.Lock();
        // bool old = gLogger->hasTime;
        // gLogger->hasTime = hasTime;
        // gLogger->mutex.UnLock();

        scopelock lock(gLogger->mutex);
        bool old = gLogger->hasTime;
        gLogger->hasTime = hasTime;
        return old;
    }

    SKY_API bool setLoggerHasFileLine(bool hasFileLine)
    {
        if (!gLogger)
            createLogger();

        scopelock lock(gLogger->mutex);
        bool old = gLogger->hasFileLine;
        gLogger->hasFileLine = hasFileLine;

        return old;
    }

    SKY_API bool setLoggerLimitFrequency(bool limitFrequency)
    {
        if (!gLogger)
            createLogger();

        scopelock lock(gLogger->mutex);
        bool old = gLogger->limitFrequency;
        gLogger->limitFrequency = limitFrequency;

        return old;
    }

    SKY_API bool hasLoggerLimitFrequency()
    {
        if (!gLogger)
            createLogger();

        return gLogger->hasLimitFrequency();
    }

    SKY_API size_t setTraceMessageThreshold(size_t threshold)
    {
        if (!gLogger)
            createLogger();

        return gLogger->setMessageThreshold(threshold);
    }

    SKY_API size_t getTraceMessageThreshold()
    {
        if (!gLogger)
            createLogger();

        return gLogger->getMessageThreshold();
    }

    SKY_API void setTraceLuaPath(const char *path)
    {
        if (!gLogger)
            createLogger();

        if (gLogger)
            gLogger->setLogerLuaPath(path);
    }

    SKY_API void registerLogAppender(LogAppender *appender)
    {
        if (!appender)
            return;

        if (!gLogger)
            createLogger();

        if (gLogger)
            gLogger->registerAppender(appender);
    }

    SKY_API void unRegisterLogAppender(LogAppender *appender)
    {
        if (!appender || !gLogger)
            return;

        gLogger->unRegisterAppender(appender);
    }

    SKY_API void output(const char *msg, LogLevel level, const char *file, int line)
    {
        if (!msg || msg[0] == 0)
            return;

        // 未创建Logger输出，则调用系统默认的调试信息输出
        if (!gLogger)
        {
    #ifdef SKY_DEBUG
            if (IsDebuggerPresent())
            {
                mostrbuf osb;
                osb << file << "(" << line << "): " << msg;
                ::OutputDebugString(osb.c_str());
            }
    #endif
        }
        else
        {
            gLogger->output(msg, level, file, line);
        }
    }

    SKY_API LogAppender *output2Console(int level, bool hasTime)
    {
        if (!gLogger)
            createLogger();
        
        if (gLogger)
        {
            ConsoleAppender * appender = new ConsoleAppender();
            appender->setLevl(level);
            appender->setHasTime(hasTime);

            registerLogAppender(appender);

            return appender;
            
        }

        return NULL;
    }

    SKY_API LogAppender *output2File(const char *filename, int level, bool hasTime)
    {

        char fn[256];
        if (!filename)
        {
            sprintf(fn, ".\\log.html");
            filename = fn;
        }

        if (filename)
        {
            if (!gLogger)
                createLogger();

            FileAppender * appender = new FileAppender();
            if (!appender->create(filename, true))
            {
                delete appender;
                return NULL;
            }

            appender->setLevl(level);
            appender->setHasTime(hasTime);

            registerLogAppender(appender);

            return appender;
        }


        return NULL;
    }

    const char *LogLevelToString(sky::LogLevel level)
    {
        switch (level)
        {
        case LogLevel_Info:
        case LogLevel_Trace:
        case LogLevel_Emphasis:
            return "";
        
        case LogLevel_Warning:
            return "WARNING: ";
        
        case LogLevel_Error:
            return "ERROR: ";


        
        default:
            return "UNKNOW";
        }

        return "UNKNOW";
    }



} // namespace sky
